// linux/tools/build.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFFER_SIZE	0x1000
#define ELF_BUF_SIZE	0x1000

// class image {
// image内存区基地址：
unsigned char * image_base_ptr = NULL;

// image内存区的当前地址：
unsigned long  image_offset = 0;

// image内存区的大小：
unsigned long   image_size = 0;

// 分配大小为size的内存区域
void image_init(unsigned long size);

// 将指定区域清0
void image_clear(unsigned long offset, unsigned long size);

// 将elf格式的可执行文件的代码提取到内存中
unsigned long image_read_elf(const char * elf_file);
unsigned long image_read_bin(const char * bin_file);

void image_read_bootsect(const char * file);
void image_read_setup(const char * file);
void image_read_system(const char * file);

// 将内存image块的代码写入指定的bin文件
unsigned long image_write_bin(const char * bin_file);

// 清空内存
void image_free(void);

// }

void die(const char * msg)
{
	fprintf(stderr, "Error: %s\n", msg);
	image_free();
	exit(1);
}
void image_init(unsigned long size)
{
	if(size <= 0) {
		die("image_init: image size error!");
	}
	if(image_base_ptr!=NULL) {
		die("image_init: remalloc memory error");
	}
	image_base_ptr = (unsigned char *)malloc(size);
	if(image_base_ptr==NULL){
		die("image_init: malloc memory error!");
	}

	image_clear(0, image_size);

	image_offset = 0;
	image_size = size;
}

void image_clear(unsigned long offset, unsigned long size)
{
	unsigned long i;
	if(offset+ size > image_size) {
		die("image_clear: offset + size > image_size!");
	}
	for(i=offset; i < size; i++){
		image_base_ptr[i] = 0;
	}
}

unsigned long image_read_elf(const char * elf_file)
{
	int elf_fd;
	int bytes_read;
	int i=0;
	unsigned char elf_buf[ELF_BUF_SIZE];
        unsigned char *base_ptr	= image_base_ptr + image_offset;
	Elf32_Ehdr *eh=(Elf32_Ehdr *)elf_buf;
	Elf32_Phdr *ph=NULL;
	unsigned long max_offset = 0;

	//以只读方式打开文件elf_file:
	if((elf_fd=open(elf_file, O_RDONLY))==-1) {
		die("image_read_elf: open elf file error!");
	}

	//读取其elf header到elf_buf缓冲区中:
	if((bytes_read=read(elf_fd,elf_buf,ELF_BUF_SIZE))!=ELF_BUF_SIZE) {
		die("image_read_elf: read elf header error!");
	}
	/*
	printf("e_type=%d\n", eh->e_type);
	printf("e_machine=%d\n", eh->e_machine);
	printf("e_entry=%d\n", eh->e_entry);
	printf("e_phoff=%d\n", eh->e_phoff);
	printf("e_shoff=%d\n", eh->e_shoff);
	printf("e_flags=%d\n", eh->e_flags);
	printf("e_ehsize=%d\n", eh->e_ehsize);
	printf("e_phentsize=%d\n", eh->e_phentsize);
	printf("e_phnum=%d\n", eh->e_phnum);
	printf("e_shentsize=%d\n", eh->e_shentsize);
	printf("e_shnum=%d\n", eh->e_shnum);
	*/
	for(i=0,ph=(Elf32_Phdr*)(elf_buf+eh->e_phoff);i<eh->e_phnum;i++,ph++) {
	/*	printf(" %d, ", i);
		printf(" type=%d, ", ph->p_type);
		printf(" offset=%d, ", ph->p_offset);
		printf(" filesz=%d, ", ph->p_filesz);
		printf(" vaddr=%d, ", ph->p_vaddr);
		printf(" memsz=%d \n ", ph->p_memsz);
	*/
		if(ph->p_type==0) continue;

		//定位文件读指针到ph->p_offset处
		lseek(elf_fd, ph->p_offset,0);

		//将写指针定位到base_ptr+ph->p_vaddr处, 
		//从elf_fd处读入ph->p_filesz大小的内容
		bytes_read = read(elf_fd, base_ptr+ph->p_vaddr, ph->p_filesz);
		if(bytes_read != ph->p_filesz) {
			die("image_read_elf: read error!");
		}
		//
		if(ph->p_memsz>ph->p_filesz) {
			image_clear(image_offset+ph->p_vaddr+ph->p_filesz, 
					ph->p_memsz-ph->p_filesz);
		}
		//
		if(max_offset < ph->p_vaddr + ph->p_memsz) {
			max_offset = ph->p_vaddr + ph->p_memsz;
		}
	}
	//设定image的最新偏移指针
	image_offset += max_offset;
	//printf(" max_offset=%d \n ", max_offset);
	close(elf_fd);
	return max_offset;
}

unsigned long image_read_bin(const char * bin_file)
{
	int bin_fd;
	unsigned long bytes_read;
	unsigned char * base_ptr = image_base_ptr + image_offset;
	bin_fd = open(bin_file, O_RDONLY);
	if(bin_fd==-1){
		die("image_read_bin: can't open the bin_file!");
	}
	bytes_read = read(bin_fd, base_ptr, image_size-image_offset);
	if(bytes_read < 0){
		die("image_read_bin: read error!");
	}
	image_offset += bytes_read;
	close(bin_fd);
	return bytes_read;
}

void image_read_bootsect(const char * file)
{
	unsigned long bytes_read = 0;
	image_offset = 0;
	bytes_read = image_read_bin(file);
	fprintf(stderr, "bootsect size: 0x%x, %dB\n", bytes_read, bytes_read);
	if(bytes_read != 512){
		die("bootsect's size must be 512B");
	}
}
void image_read_setup(const char * file)
{
	unsigned long bytes_read=0;
	image_offset = 512;
	bytes_read = image_read_bin(file);
	fprintf(stderr, "setup size: 0x%x, %dB\n", bytes_read, bytes_read);
	if(bytes_read > 512 * 4){
		die("setup's size must be 2048B!");
	}
}
void image_read_system(const char *file)
{
	unsigned long bytes_read=0;
	image_offset = 512+512*4;
	bytes_read = image_read_elf(file);
	fprintf(stderr, "system size: 0x%x, %dB, %dKB\n", bytes_read, bytes_read, bytes_read/1024);
	if(bytes_read > (512*1024)){
		die("system's size too big!");
	}
}

unsigned long image_write_bin(const char * bin_file)
{
	int bin_fd;
	int bytes_write;
	if(bin_file==NULL){
		bin_fd = 1;
	}else{
		bin_fd=open(bin_file, O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
	}
	if(bin_fd==-1) {
		die("image_write_bin: open file error!");
	}
	bytes_write=write(bin_fd, image_base_ptr, image_offset);
	if(bytes_write != image_offset){
		die("image_write_bin: write file error!");
	}
	close(bin_fd);
	return image_offset;
}
void image_write_zImage(const char * file)
{
	unsigned long bytes_write = 0;
	bytes_write = image_write_bin(file);
	fprintf(stderr, "zImage size: 0x%x, %dB, %dKB\n", bytes_write, bytes_write, bytes_write/1024);
}
void image_free(void)
{
	if(image_base_ptr!=NULL){
		free(image_base_ptr);
		image_base_ptr = NULL;
	}
	image_offset = 0;
	image_size = 0;
}
void usage(void)
{
	fprintf(stderr, "Usage: tools/build boot/bootsect boot/setup tools/system\n\r");
}
int main(int argc, char **argv)
{
	if(argc!=4){
		usage();
		die("main: argc != 4");
	}

	image_init(1<<20);
	image_read_bootsect(argv[1]);
	image_read_setup(argv[2]);
	image_read_system(argv[3]);
	image_write_zImage(NULL);
	image_free();
}

